﻿#include <unordered_set>
#include <algorithm>

#include "Reflectable.h"
#include "statemachine.h"
#include "metaproperty.h"
#include "eventloop.h"


#define D_ON_STARTED_R if (IsStarted())\
	{\
	std::cerr << "State:Error Can't Execute" << __func__ << "While Started!" << std::endl;\
	return {};\
	}\

#define D_ON_STARTED if (IsStarted())\
	{\
	std::cerr << "State:Error Can't Execute" << __func__ << "While Started!" << std::endl;\
	return;\
	}\

#define E_ON_STARTED_R if (!IsStarted())\
	{\
	std::cerr << "State:Error Can't Execute" << __func__ << "While Not Started!" << std::endl;\
	return {};\
	}\

#define E_ON_STARTED if (!IsStarted())\
	{\
	std::cerr << "State:Error Can't Execute" << __func__ << "While Not Started!" << std::endl;\
	return;\
	}\

class StateMachine::State::StatePrivate
{
public:
	static ::StateMachine::State* FindCommonParent(::StateMachine::State* s1, ::StateMachine::State* s2);
	static bool CheckActive(::StateMachine::State* s);
	static std::vector<::StateMachine::State*> GetParents(::StateMachine::State* s1);
	static void SetUpState(::StateMachine::State* top,const std::vector<::StateMachine::State*>& s);
	static void LeaveState(::StateMachine::State* s);
	static void EnterState(::StateMachine::State* s);
	static bool StartCheck(::StateMachine::State* s);
};

StateMachine::State::State()
{
	type = StateType::Single;
	m_parent = NULL;
	m_top = NULL;
	m_isActive = false;
	m_currentState = NULL;
}

StateMachine::State::~State()
{
	if (m_parent) {
		m_parent->m_children.erase(this);
		m_parent = NULL;
	}

	auto c = m_children;
	for (auto i : c) {
		delete i;
	}

	auto t = m_Translations;
	for (auto i : t) {
		delete i;
	}
}

StateMachine::State * StateMachine::State::CreateSubState()
{
	D_ON_STARTED_R
	State* result = new State();

	result->m_parent = this;
	result->m_top = m_top;
	m_children.insert(result);

	if (m_currentState == NULL && StateType::Single == type) {
		SetInitState(result);
	}

	return result;
}

StateMachine::State::StateType StateMachine::State::GetType() const
{
	return type;
}

void StateMachine::State::SetType(StateType type)
{
	D_ON_STARTED

	this->type = type;
}

std::set<StateMachine::State*> StateMachine::State::children()
{
	return m_children;
}

StateMachine::State * StateMachine::State::parent()
{
	return m_parent;
}

StateMachine::StateMachine * StateMachine::State::TopNode()
{
	return m_top;
}

void StateMachine::State::SetInitState(State * state)
{
	D_ON_STARTED;

	if (state == NULL) {
		return;
	}

	if (type == Parallel) {
		return;
	}

	if (state->parent() != this) {
		return;
	}

	m_currentState = state;
}

StateMachine::Translation * StateMachine::State::AddTranslation(GenericSignal * signal, State * target)
{
	D_ON_STARTED_R
	Translation* res = new Translation();

	res->m_state = this;
	res->m_target = target;

	res->m_registry += signal->UnSafeBind([=](const void**) {
		OnTranslate(res);
	});

	m_Translations.insert(res);

	return res;
}

void StateMachine::State::SetProperty(void * target, MetaInfo info, const char * property, Variant value)
{
	SetProperty(target, info, NULL, property, value);
}

void StateMachine::State::SetProperty(Reflectable * target, const char * property, Variant value)
{
	SetProperty(dynamic_cast<void*>(target), target->GetMetaInfo(),NULL,property,value);
}

void StateMachine::State::SetProperty(ReflectableObject * target, const char * property, Variant value)
{
	SetProperty(dynamic_cast<void*>(target), target->GetMetaInfo(),target, property, value);
}

bool StateMachine::State::isActive() const
{
	return m_isActive;
}

void StateMachine::State::OnTranslate(Translation * trans)
{
	if (!IsStarted()) {
		return;
	}

	if (!isActive()) {
		return;
	}

	auto target = trans->m_target;
	std::vector<State*> links;
	auto p = target;
	while (p->parent())
	{
		links.push_back(p);
		p = p->parent();
	}

	std::reverse(links.begin(), links.end());

	EventLoop::PostEvent(m_top, new ThreadSafe::InvokeFunctionEvent([=]() {
		StatePrivate::SetUpState(m_top, links);
	}));
}

void StateMachine::State::StateEntered()
{
	EnteredState();
}

void StateMachine::State::StateLeaved()
{
	LeavedState();
}

bool StateMachine::State::IsStarted()
{
	return m_top->IsStarted();
}

void StateMachine::State::SetProperty(void * target, MetaInfo info, ThreadSafe::ThreadSafeBase * safe, const char * property, Variant value)
{
	int propertyIdx = info.IndexOfProperty(property);
	EnteredState.UnSafeBind([=](const void**) {
		info.GetProperty(propertyIdx).SetValue(target, value);
	}, safe);
}

StateMachine::StateMachine::StateMachine()
{
	m_top = this;
	m_bIsStarted = false;
}

StateMachine::StateMachine::~StateMachine()
{
	Stop();
}

void StateMachine::StateMachine::Start()
{
	if (m_bIsStarted != true) {
		if (!StartCheck()) {
			return;
		}

		m_bIsStarted = true;

		StatePrivate::EnterState(this);
	}
}

void StateMachine::StateMachine::Stop()
{
	if (m_bIsStarted != false) {
		m_bIsStarted = false;

		StatePrivate::LeaveState(this);
	}
}

bool StateMachine::StateMachine::IsStarted() const
{
	return m_bIsStarted;
}

bool StateMachine::StateMachine::StartCheck()
{
	return StatePrivate::StartCheck(this);
}

StateMachine::Translation::~Translation()
{
	m_state->m_Translations.erase(this);
}

StateMachine::State* StateMachine::State::StatePrivate::FindCommonParent(StateMachine::State* s1, StateMachine::State* s2) {

	while (s1 && s2)
	{
		if (s1 == s2) {
			return s1;
		}

		s1 = s1->parent();

		if (s1 == s2) {
			return s1;
		}

		s2 = s2->parent();
	}

	return NULL;
}

bool StateMachine::State::StatePrivate::CheckActive(StateMachine::State * s)
{
	auto parent = s->parent();

	bool isPointedThis = {};

	if (parent->GetType() == StateMachine::State::StateType::Parallel)
	{
		isPointedThis = true;
	}
	else if (parent->GetType() == StateMachine::State::StateType::Single) {
		if (parent->m_currentState == s) {
			isPointedThis = true;
		}
		else {
			isPointedThis = false;
		}
	}

	if (isPointedThis) {
		if (parent == s->TopNode()) {
			return true;
		}
		else {
			CheckActive(parent);
		}
	}
	else {
		return false;
	}

	return false;
}

std::vector<::StateMachine::State*> StateMachine::State::StatePrivate::GetParents(::StateMachine::State * s1)
{
	std::vector<::StateMachine::State*> ret;
	s1 = s1->parent();
	while (s1)
	{
		ret.push_back(s1);
		s1 = s1->parent();
	}

	return ret;
}

void StateMachine::State::StatePrivate::SetUpState(::StateMachine::State * top,const std::vector<::StateMachine::State*>& s)
{
	//找到第一个切换的节点

	int changed = -1;

	for (int i = 0; i < s.size(); i++) {
		auto tar = s[i];
		auto cur = tar->parent();

		bool isChanged = false;
		
		if (cur->type == State::StateType::Parallel)
		{

		}
		else if (cur->type == State::Single) {
			if (cur->m_currentState != tar)
			{
				changed = i;
			}
		}
	}

	if (changed == -1) {
		return;//没有需要切换的节点
	}

	{//离开原状态
		auto tar = s[changed];
		auto p = tar->parent();
		auto old = p->m_currentState;
		p->m_currentState = tar;
		LeaveState(old);
	}

	{//进入新状态
		int i = changed;
		for (; i < s.size(); i++) {
			auto tar = s[i];
			auto p = tar->parent();
			if (p->type == State::Single)
			{
				p->m_currentState = tar;
			}
		}
		EnterState(s[changed]);
	}
}

void StateMachine::State::StatePrivate::LeaveState(::StateMachine::State * s)
{
	s->m_isActive = false;
	s->StateLeaved();
	
	if (s->type == State::Single)
	{
		if (s->m_currentState) {
			LeaveState(s->m_currentState);
		}
	}
	else if (s->type == State::Parallel) {
		for (auto i : s->m_children) {
			LeaveState(i);
		}
	}
}

void StateMachine::State::StatePrivate::EnterState(::StateMachine::State * s)
{
	s->m_isActive = true;
	s->StateEntered();

	if (s->type == State::Single)
	{
		if (s->m_currentState) {
			EnterState(s->m_currentState);
		}
	}
	else if (s->type == State::Parallel) {
		for (auto i : s->m_children) {
			EnterState(i);
		}
	}
}

bool StateMachine::State::StatePrivate::StartCheck(::StateMachine::State * s)
{
	bool ret = true;

	if (s->GetType() == ::StateMachine::State::Single) {
		if (!s->m_children.empty()) {
			if (s->m_currentState == NULL) {
				ret = false;
			}
		}
	}

	if (!ret) {
		std::cerr << "Error!Some None Leaf State have not set init State! Start Failed!" << std::endl;
		return false;
	}

	for (auto i : s->m_children) {
		bool ret = StartCheck(i);
		if (!ret) {
			return false;
		}
	}

	return true;
}
